#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
__title__ = ''
__author__ = 'HaiFeng'
__mtime__ = '2016/8/16'
"""

import time
import logging.handlers
from py_at.at_struct import *
from py_at.OrderItem import OrderItem
from py_at.Enums import DirectType,OffsetType,IntervalType
from py_at.Bar import Bar
from py_at.switch import switch
from py_at.Tool import *
import datetime
import numpy as np
import talib


class Data(object):
    """数据类, 策略继承此类"""

    def __init__(self, stra_barupdate='', stra_onorder=''):
        '''初始所有变量'''
        self.BarUpdate = stra_barupdate
        self.OnOrder = stra_onorder
        """初始所有变量"""
        # 序列变量
        #self.Bars = []
        self.SingleOrderOneBar = False
        self.Bars =np.array([])
        self.D = np.array([])
        self.H = np.array([])
        self.L = np.array([])
        self.O = np.array([])
        self.C = np.array([])
        self.V = np.array([])
        self.I = np.array([])

        # 每日的基础数据
        self.DateD = []
        self.OpenD = []
        self.HighD = []
        self.LowD = []
        self.CloseD = []

        self.Instrument = ''
        self.Interval = 1
        self.IntervalType = IntervalType.Minute


        self.IndexDict = {}


        self.Orders = []
        self.PriceTick = 1 #  最小跳
        self._lastOrder = OrderItem()
        self.CurrMin = None # 当前分钟
        self.biaozhi = False






    # 当前策略相关
    @property
    def AvgEntryPriceShort(self):
        return self._lastOrder.AvgEntryPriceShort

    @property
    def AvgEntryPriceLong(self):
        return self._lastOrder.AvgEntryPriceLong

    @property
    def PositionLong(self):
        return self._lastOrder.PositionLong

    @property
    def PositionShort(self):
        return self._lastOrder.PositionShort

    @property
    def EntryDateLong(self):
        return self._lastOrder.EntryDateLong

    @property
    def EntryPriceLong(self):
        return self._lastOrder.EntryPriceLong

    @property
    def ExitDateShort(self):
        return self._lastOrder.ExitDateShort

    @property
    def ExitPriceShort(self):
        return self._lastOrder.ExitPriceShort

    @property
    def EntryDateShort(self):
        return self._lastOrder.EntryDateShort

    @property
    def EntryPriceShort(self):
        return self._lastOrder.EntryPriceShort

    @property
    def ExitDateLong(self):
        return self._lastOrder.ExitDateLong

    @property
    def ExitPriceLong(self):
        return self._lastOrder.ExitPriceLong

    @property
    def LastEntryDateShort(self):
        return self._lastOrder.LastEntryDateShort

    @property
    def LastEntryPriceShort(self):
        return self._lastOrder.LastEntryPriceShort

    @property
    def LastEntryDateLong(self):
        return self._lastOrder.LastEntryDateLong

    @property
    def LastEntryPriceLong(self):
        return self._lastOrder.LastEntryPriceLong

    @property
    def IndexEntryLong(self):
        """
        :return:多开仓bar的索引
        """
        return self._lastOrder.IndexEntryLong

    @property
    def IndexEntryShort(self):
        """
        :return:空开仓bar的索引
        """
        return self._lastOrder.IndexEntryShort

    @property
    def IndexLastEntryLong(self):
        return self._lastOrder.IndexLastEntryLong

    @property
    def IndexLastEntryShort(self):
        return self._lastOrder.IndexLastEntryShort

    @property
    def IndexExitLong(self):
        return self._lastOrder.IndexExitLong

    @property
    def IndexExitShort(self):
        return self._lastOrder.IndexExitShort

    @property
    def BarsSinceEntryLong(self):
        '''获取当前多仓第一次开仓到当前bar的 k线根数'''
        return len(self.Bars)-self.IndexEntryLong

    @property
    def BarsSinceEntryShort(self):
        '''获取当前空仓第一次开仓到当前bar的 k线根数'''
        return len(self.Bars) - self.IndexEntryShort

    @property
    def BarsSinceExitLong(self):
        '''获得最近多仓平仓位置到当前位置的Bar计数'''
        return len(self.Bars) - self.IndexExitLong

    @property
    def BarsSinceExitShort(self):
        '''获得最近空仓平仓位置到当前位置的Bar计数'''
        return len(self.Bars) - self.IndexExitShort

    #净持仓
    @property
    def Position(self):
        return self.PositionLong - self.PositionShort

    # 返回当前最后一个Bar
    @property
    def CurrentBar(self):
        return max(len(self.Bars) - 1, 0)

    def on_tick(self, tick):
        ''' 取此tick对应的分钟时间
            如果生成一分钟数据  那么直接更新__new_min_bar__(bar)
            否则在此1分钟中处理高开低手数据
        '''
        if not tick.Instrument in ['btc_usd', 'ltc_usd', 'eth_usd', 'etc_usd', 'bch_usd', 'xrp_usd', 'eos_usd', 'btg_usd']:  #如果是虚拟货币的话 不需要进行数据清理
            if(tick.UpdateTime.hour not in [9,10,11,13,14,21,22,23,24,00,1,2]):
                return
            #10:15:01 已经属于脏数据所以使用大于等于      10:29:59也属于   但是10:30:00就属于正常数据了
            if(tick.UpdateTime.hour==10 and tick.UpdateTime.minute>14) and (tick.UpdateTime.hour==10 and tick.UpdateTime.minute<30):
                return
            # 若有股指或者别的应该排除  但是这里先写死再说
            if (tick.UpdateTime.hour == 11 and tick.UpdateTime.minute >29) or (tick.UpdateTime.hour == 13 and tick.UpdateTime.minute < 30):
                return

        #bar_time = tick.UpdateTime.strftime("%Y%m%d %H:%M:00")
        bar_time = tick.UpdateTime.strftime("%Y-%m-%d %H:%M:00")

        if len(self.Bars) == 0 or self.CurrMin != bar_time:  # 新数据是否等于上个1分钟数据
            # bar_time, h, l, o, c, v, i, a)
            bar = Bar(bar_time, tick.LastPrice, tick.LastPrice, tick.LastPrice, tick.LastPrice, tick.Volume,
                      tick.OpenInterest,tick.TradingDay,tick.Instrument)
            bar._pre_volume = tick.Volume
            #print('%s 生成分钟数%s 当前去掉秒bar时间%s 带秒时间%s' %(tick.Instrument,self.Bars[-1].D,time.strftime("%Y-%m-%d %H:%M:00", tick.UpdateTime),time.strftime('%Y-%m-%d %H:%M:%S', tick.UpdateTime)))
            self.__new_min_bar__(bar)  # 新来的1分钟K线
            self.CurrMin = bar_time    #当前1分钟数
        else:
            bar = self.Bars[-1]
            bar.H = max(bar.H, tick.LastPrice)
            bar.L = min(bar.L, tick.LastPrice)
            bar.C = tick.LastPrice
            #bar.V = tick.Volume - bar._pre_volume   这个为什么是减呢 不是应该加吗
            try:   #如果数据库初始化bar._pre_volume这个没有设置，所以在这个地方进行一个判断 如果没有设置了  直接得到当前tick 以防止异常
                bar.V = tick.Volume + bar._pre_volume
                bar._pre_volume = tick.Volume
            except AttributeError:
                bar._pre_volume=tick.Volume
            bar.I = tick.OpenInterest
            bar.A = tick.AveragePrice
            self.__update_bar__(bar)  # 1分钟bar已经更新完成
    def __new_min_bar__(self, bar):
        """有新1分钟min_bar添加"""

        #bar_time = time.strptime(bar.D, "%Y%m%d %H:%M:%S")  #转化为元祖  使用这个方法比较费时 所以使用下面的方法了  一切为了效率
        if(not isinstance(bar.D,datetime.datetime)):
            bar_time = customized_parse(bar.D)
        else:
            bar_time = bar.D
        year = bar_time.year
        mon = bar_time.month
        day = bar_time.day
        hour = bar_time.hour
        mins = bar_time.minute
        for case in switch(self.IntervalType):
            if case(IntervalType.Minute):
                mins = mins // self.Interval * self.Interval  # //整除
                break
            if case(IntervalType.Hour):
                hour = hour // self.Interval
                mins = 0
                break
            if case(IntervalType.Day):
                hour = 0
                mins = 0
                break
            if case(IntervalType.Month):
                hour = 0
                mins = 0
                day = 1
                break
            if case(IntervalType.Year):
                hour = 0
                mins = 0
                day = 1
                mon = 1
                break
            if case(IntervalType.Week):
                hour = 0
                mins = 0
                # 用周号替换日期
                day = bar_time.strftime('%U')    #得到当前是第几周
                break
        #构造日期时间
        #bar_time = time.strptime('{0}-{1}-{2} {3}:{4}'.format(year, mon, day, hour, mins), '%Y-%m-%d %H:%M')   #把一个时间字符串解析为时间元组
        bar_time = customized_parse('{0}{1}{2} {3}:{4}'.format(buwei(year), buwei(mon), buwei(day), buwei(hour), buwei(mins)))
        # time -> str  时间转字符串
        #bar_time = time.strftime('%Y-%m-%d %H:%M:%S', bar_time) #把一个元祖输出时间字符串
        bar_time = bar_time.strftime('%Y-%m-%d %H:%M:%S')

        if len(self.Bars) == 0 or self.Bars[-1].D != bar_time:  # 新bar
            bar.D = bar_time
            #self.Bars.append(bar)
            self.Bars = np.append(self.Bars,bar)

            self.D = np.append(self.D, bar.D)
            self.H = np.append(self.H, bar.H)
            self.L = np.append(self.L, bar.L)
            self.O = np.append(self.O, bar.O)
            self.C = np.append(self.C, bar.C)
            self.V = np.append(self.V, bar.V)
            self.I = np.append(self.I, bar.I)
            # if(len(self.Bars)>3):
            #     print("开%s 高%s 低%s 收%s 时间%s" %(self.O[-2],self.H[-2],self.L[-2],self.C[-2],self.D[-2]))
            #     print("开%s 高%s 低%s 收%s 时间%s bar" %(self.Bars[-2].O,self.Bars[-2].H,self.Bars[-2].L,self.Bars[-2].C,self.D[-2]))
        else:
            old_bar = self.Bars[-1]                               # 更新高低量 用于构造bar
            self.H[-1] = old_bar.H = max(bar.H, old_bar.H)
            self.L[-1] = old_bar.L = min(bar.L, old_bar.L)
            self.C[-1] = old_bar.C = bar.C
            old_bar.V += bar.V
            self.V[-1] = old_bar.V
            self.I[-1] = old_bar.I = bar.I
        # bar.A = tick.AveragePrice
        # 日线数据处理               和 bar 构造差不多  如果data相等那么更新  否则插入
        #date = '{0}-{1}-{2}'.format(year, mon, day)

        date = bar.TD  #原来是用真实日期做的HighD  现在更改为交易日期

        # 当前时间是否和上一个时间的年月日匹配  如果不匹配证明是新的一天bar  那么更新日线级别
        # 2015-05-06 11:00   2015-05-06 21:00   2015-05-06 13:00   .............
        # 如果碰到2015-05-07 那么证明上一天结束  插入新的  否则更新最高最低收盘
        if len(self.DateD) == 0 or self.DateD[0] != date:
            self.DateD.insert(0, date)
            self.OpenD.insert(0, bar.O)
            self.HighD.insert(0, bar.H)
            self.LowD.insert(0, bar.L)
            self.CloseD.insert(0, bar.C)
        else:            # 如果还是当前天 那么就更新最高最低和收盘
            self.HighD[0] = max(self.HighD[0], bar.H)
            self.LowD[0] = min(self.LowD[0], bar.L)
            self.CloseD[0] = bar.C

        self.BarUpdate(self,bar)

    def __update_bar__(self, bar):
        """更新当前数据序列"""

        self.D[-1] = bar.D
        self.H[-1] = bar.H
        self.L[-1] = bar.L
        self.O[-1] = bar.O
        self.C[-1] = bar.C
        self.V[-1] = bar.V
        self.I[-1] = bar.I

        self.HighD[-1] = max(self.HighD[-1], bar.H)
        self.LowD[-1] = min(self.LowD[-1], bar.L)
        self.CloseD[-1] = bar.C

        self.BarUpdate(self,bar)

    # def BarUpdate(self, bar):
    #     """数据更新时调用此函数"""
    #     pass

    def OnOrder(self, stra, order):
        """继承类中实现此函数,有策略信号产生时调用"""
        pass

    def __order__(self, direction, offset, price, volume, remark):
        """策略执行信号"""


        #当前bar不能重复发单  否则不执行------------------2018-5-8注释掉
        # if (direction.name=='Sell'):
        #     if self.SingleOrderOneBar and (self.LastEntryDateLong==self.D[-1] or self.LastEntryDateShort==self.D[-1] or self.ExitDateShort == self.D[-1]):
        #         return
        # if (direction.name == 'Buy'):
        #     if self.SingleOrderOneBar and (self.LastEntryDateLong == self.D[-1] or self.LastEntryDateShort == self.D[-1] or self.ExitDateLong == self.D[-1]):
        #         return
        # # 有些策略生成的价格有小数  这里是进行检测并规范报价的
        # if price%self.PriceTick!=0:
        #     if(direction.name=="Sell"):
        #         price = price- (price % self.PriceTick)
        #     elif(direction.name=="Buy"):
        #         price = price + (self.PriceTick-(price % self.PriceTick))
        #---------------------------------------------------2018-5-8 注释掉  添加了下面的
        if self.SingleOrderOneBar:
            # 平仓后允许开仓
            if self.ExitDateShort == self.D[-1] and (not (direction == DirectType.Buy and offset == OffsetType.Open)):
                return
            if self.ExitDateLong == self.D[-1] and (not (direction == DirectType.Sell and offset == OffsetType.Open)):
                return
            if self.LastEntryDateLong == self.D[-1] or self.LastEntryDateShort == self.D[-1]:
                return
        #----------------------------------------------------2018-5-8 添加上面

        #构造orderItem（）开始
        order = OrderItem()
        order.Instrument = self.Instrument
        order.DateTime = self.D[-1]
        order.Direction = direction
        order.Offset = offset
        order.Price = price
        order.Volume = volume
        order.Remark = remark

        self.Orders.append(order)

        # 处理策略相关属性
        order.IndexEntryLong = self._lastOrder.IndexEntryLong
        order.IndexExitLong = self._lastOrder.IndexExitLong
        order.IndexEntryShort = self._lastOrder.IndexEntryShort
        order.IndexExitShort = self._lastOrder.IndexExitShort
        order.IndexLastEntryLong = self._lastOrder.IndexLastEntryLong
        order.IndexLastEntryShort = self._lastOrder.IndexLastEntryShort
        order.AvgEntryPriceLong = self._lastOrder.AvgEntryPriceLong
        order.AvgEntryPriceShort = self._lastOrder.AvgEntryPriceShort
        order.PositionLong = self._lastOrder.PositionLong
        order.PositionShort = self._lastOrder.PositionShort
        order.EntryDateLong = self._lastOrder.EntryDateLong
        order.EntryDateShort = self._lastOrder.EntryDateShort
        order.EntryPriceLong = self._lastOrder.EntryPriceLong
        order.EntryPriceShort = self._lastOrder.EntryPriceShort
        order.ExitDateLong = self._lastOrder.ExitDateLong
        order.ExitDateShort = self._lastOrder.ExitDateShort
        order.ExitPriceLong = self._lastOrder.ExitPriceLong
        order.ExitPriceShort = self._lastOrder.ExitPriceShort
        order.LastEntryDateLong = self._lastOrder.LastEntryDateLong
        order.LastEntryDateShort = self._lastOrder.LastEntryDateShort
        order.LastEntryPriceLong = self._lastOrder.LastEntryPriceLong
        order.LastEntryPriceShort = self._lastOrder.LastEntryPriceShort

        diroff = '{0}-{1}'.format(order.Direction.name, order.Offset.name)
        for case in switch(diroff):
            if case('Buy-Open'):
                order.PositionLong = round(order.PositionLong +order.Volume,2)
                order.AvgEntryPriceLong = (
                                              self._lastOrder.PositionLong * self._lastOrder.AvgEntryPriceLong + order.Volume * order.Price) / (
                                              self._lastOrder.PositionLong + order.Volume)
                if self._lastOrder.PositionLong == 0:
                    order.IndexEntryLong = len(self.Bars) - 1
                    order.EntryDateLong = self.D[-1]  # str '20160630 21:25:00'
                    order.EntryPriceLong = order.Price
                order.IndexLastEntryLong = len(self.Bars) - 1
                order.LastEntryPriceLong = order.Price
                order.LastEntryDateLong = self.D[-1]
                break

            if case('Buy-Close'):
                c_lots = min(self._lastOrder.PositionShort, order.Volume)  # 能够平掉的数量
                if c_lots <= 0:  # 无仓可平
                    print('平仓量>持仓量')
                    break
                order.PositionShort -= c_lots

                order.IndexExitShort = len(self.Bars) - 1
                order.ExitDateShort = self.D[-1]
                order.ExitPriceShort = order.Price
                if order.PositionShort == 0:
                    order.AvgEntryPriceShort = 0
                break

            if case('Sell-Open'):
                order.PositionShort = round(order.PositionShort + order.Volume,2)
                order.AvgEntryPriceShort = (
                                               self._lastOrder.PositionShort * self._lastOrder.AvgEntryPriceShort + order.Volume * order.Price) / (
                                               self._lastOrder.PositionShort + order.Volume)
                if self._lastOrder.PositionShort == 0:
                    order.IndexEntryShort = len(self.Bars) - 1
                    order.EntryDateShort = self.D[-1]  # time or double or str ???
                    order.EntryPriceShort = order.Price
                order.IndexLastEntryShort = len(self.Bars) - 1
                order.LastEntryPriceShort = order.Price
                order.LastEntryDateShort = self.D[-1]
                break

            if case('Sell-Close'):
                c_lots = min(self._lastOrder.PositionLong, order.Volume)  # 能够平掉的数量
                if c_lots <= 0:  # 无仓可平
                    print('平仓量>持仓量')
                    break
                order.PositionLong -= c_lots

                order.IndexExitLong = len(self.Bars) - 1
                order.ExitDateLong = self.D[-1]
                order.ExitPriceLong = order.Price
                if order.PositionLong == 0:
                    order.AvgEntryPriceLong = 0;
                break

        self._lastOrder = order
        #TODO  这里可以加一个策略交易日志输出 下面注释的那条代码
        #  print("时间%s %s" %(self.D[-1],remark))
        self.OnOrder(self, order)
        #  订单过后回传给OnOrder    OnOrder在py_at_adapter_test.py 里面又被重新赋值了   所以调用的py_at_adapter_text.py里面的on_order方法

    def Buy(self, price, volume, remark):
        """买开"""
        self.__order__(DirectType.Buy, OffsetType.Open, price, volume, remark)

    def Sell(self, price, volume, remark):
        """卖平"""
        self.__order__(DirectType.Sell, OffsetType.Close, price, volume, remark)

    def SellShort(self, price, volume, remark):
        """卖开"""
        self.__order__(DirectType.Sell, OffsetType.Open, price, volume, remark)

    def BuyToCover(self, price, volume, remark):
        """买平"""
        self.__order__(DirectType.Buy, OffsetType.Close, price, volume, remark)

    # 对日志进行记录
    def writeLog(self,log):
        logInfo= logging.getLogger('Celue')
        if self.EnableOrder == True:
            logInfo.info(log)
        else:
            print(log)

    #另一种日志
    def writeStrategyLog(self,log):
        pass
        # if self.EnableOrder == True:
        #     with open(self.ID+'.txt','a') as f:
        #         f.write(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) +"  " + log)
        #         f.write("\n")
        # else:
        #     print(log)
        print(log)

